﻿
namespace Microsoft.Xna.Framework.Graphics
{
	/// <summary>
	/// Contains a basic rendering effect.
	/// </summary>
    public class BasicEffect : Effect
	{
		#region Fields
		private static byte[] Code;
		private EffectParameter diffuseColorParam;
		private EffectParameter emissiveColorParam;
		private EffectParameter eyePositionParam;
		private EffectParameter fogColorParam;
		private EffectParameter fogVectorParam;
		private DirectionalLight light0;
		private DirectionalLight light1;
		private DirectionalLight light2;
		private EffectParameter shaderIndexParam;
		private EffectParameter specularColorParam;
		private EffectParameter specularPowerParam;
		private EffectParameter textureParam;
		private EffectParameter worldInverseTransposeParam;
		private EffectParameter worldParam;
		private EffectParameter worldViewProjParam;
		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets the material alpha which determines its transparency. Range is between 1 (fully opaque) and 0 (fully transparent).
		/// </summary>
		public float Alpha { get; set; }

		/// <summary>
		/// Gets or sets the ambient color for a light, the range of color values is from 0 to 1.
		/// </summary>
		public Vector3 AmbientLightColor { get; set; }

		/// <summary>
		/// Gets or sets the diffuse color for a material, the range of color values is from 0 to 1.
		/// </summary>
		public Vector3 DiffuseColor { get; set; }

		/// <summary>
		/// Gets the first directional light for this effect.
		/// </summary>
		public DirectionalLight DirectionalLight0 { get { return light0; } }

		/// <summary>
		/// Gets the second directional light for this effect.
		/// </summary>
		public DirectionalLight DirectionalLight1 { get { return light1; } }

		/// <summary>
		/// Gets the third directional light for this effect.
		/// </summary>
		public DirectionalLight DirectionalLight2 { get { return light2; } }

		/// <summary>
		/// Gets or sets the emissive color for a material, the range of color values is from 0 to 1.
		/// </summary>
		public Vector3 EmissiveColor { get; set; }

		/// <summary>
		/// Gets or sets the fog color, the range of color values is from 0 to 1.
		/// </summary>
		public Vector3 FogColor { get; set; }

		/// <summary>
		/// Enables fog.
		/// </summary>
		public bool FogEnabled { get; set; }

		/// <summary>
		/// Gets or sets the maximum z value for fog.
		/// </summary>
		public float FogEnd { get; set; }

		/// <summary>
		/// Gets or sets the minimum z value for fog.
		/// </summary>
		public float FogStart { get; set; }

		/// <summary>
		/// Enables lighting for this effect.
		/// </summary>
		public bool LightingEnabled { get; set; }

		/// <summary>
		/// Gets or sets a value indicating that per-pixel lighting should be used if it is available for the current adapter. Per-pixel lighting is available if a graphics adapter supports Pixel Shader Model 2.0.
		/// </summary>
		public bool PreferPerPixelLighting { get; set; }

		/// <summary>
		/// 
		/// </summary>
		public Matrix Projection { get; set; }

		/// <summary>
		/// Gets or sets the specular color for a material, the range of color values is from 0 to 1.
		/// </summary>
		public Vector3 SpecularColor { get; set; }

		/// <summary>
		/// Gets or sets the specular power of this effect material.
		/// </summary>
		public float SpecularPower { get; set; }

		/// <summary>
		/// Gets or sets a texture to be applied by this effect.
		/// </summary>
		public Texture2D Texture
		{
			get { return this.textureParam.GetValueTexture2D(); }
			set { this.textureParam.SetValue(value); }
		}

		/// <summary>
		/// Enables textures for this effect.
		/// </summary>
		public bool TextureEnabled { get; set; }

		/// <summary>
		/// Enables use vertex colors for this effect.
		/// </summary>
		public bool VertexColorEnabled { get; set; }

		/// <summary>
		/// 
		/// </summary>
		public Matrix View { get; set; }

		/// <summary>
		/// 
		/// </summary>
		public Matrix World { get; set; }
		#endregion

		#region Constructors
		/// <summary>
		/// Creates an instance of this object.
		/// </summary>
		/// <param name="cloneSource">An instance of a object to copy initialization data from.</param>
		protected BasicEffect(BasicEffect cloneSource)
			: base(cloneSource)
		{
			this.World = Matrix.Identity;
			this.View = Matrix.Identity;
			this.Projection = Matrix.Identity;
			this.DiffuseColor = Vector3.One;
			this.EmissiveColor = Vector3.Zero;
			this.AmbientLightColor = Vector3.Zero;
			this.Alpha = 1f;
			this.FogEnd = 1f;
			this.CacheEffectParameters(cloneSource);
			this.LightingEnabled = cloneSource.LightingEnabled;
			this.PreferPerPixelLighting = cloneSource.PreferPerPixelLighting;
			this.FogEnabled = cloneSource.FogEnabled;
			this.TextureEnabled = cloneSource.TextureEnabled;
			this.VertexColorEnabled = cloneSource.VertexColorEnabled;
			this.World = cloneSource.World;
			this.View = cloneSource.View;
			this.Projection = cloneSource.Projection;
			this.DiffuseColor = cloneSource.DiffuseColor;
			this.EmissiveColor = cloneSource.EmissiveColor;
			this.AmbientLightColor = cloneSource.AmbientLightColor;
			this.Alpha = cloneSource.Alpha;
			this.FogStart = cloneSource.FogStart;
			this.FogEnd = cloneSource.FogEnd;
		}

		/// <summary>
		/// Creates an instance of this object.
		/// </summary>
		/// <param name="device">A device.</param>
		public BasicEffect(GraphicsDevice device)
			: base(device, Code)
		{
			this.World = Matrix.Identity;
			this.View = Matrix.Identity;
			this.Projection = Matrix.Identity;
			this.DiffuseColor = Vector3.One;
			this.EmissiveColor = Vector3.Zero;
			this.AmbientLightColor = Vector3.Zero;
			this.Alpha = 1f;
			this.FogEnd = 1f;
			this.CacheEffectParameters(null);
			this.DirectionalLight0.Enabled = true;
			this.SpecularColor = Vector3.One;
			this.SpecularPower = 16f;
		}
		#endregion

		private void CacheEffectParameters(BasicEffect cloneSource)
		{
			this.textureParam = base.Parameters["Texture"];
			this.diffuseColorParam = base.Parameters["DiffuseColor"];
			this.emissiveColorParam = base.Parameters["EmissiveColor"];
			this.specularColorParam = base.Parameters["SpecularColor"];
			this.specularPowerParam = base.Parameters["SpecularPower"];
			this.eyePositionParam = base.Parameters["EyePosition"];
			this.fogColorParam = base.Parameters["FogColor"];
			this.fogVectorParam = base.Parameters["FogVector"];
			this.worldParam = base.Parameters["World"];
			this.worldInverseTransposeParam = base.Parameters["WorldInverseTranspose"];
			this.worldViewProjParam = base.Parameters["WorldViewProj"];
			this.shaderIndexParam = base.Parameters["ShaderIndex"];
			this.light0 = new DirectionalLight(base.Parameters["DirLight0Direction"], base.Parameters["DirLight0DiffuseColor"], base.Parameters["DirLight0SpecularColor"], (cloneSource != null) ? cloneSource.light0 : null);
			this.light1 = new DirectionalLight(base.Parameters["DirLight1Direction"], base.Parameters["DirLight1DiffuseColor"], base.Parameters["DirLight1SpecularColor"], (cloneSource != null) ? cloneSource.light1 : null);
			this.light2 = new DirectionalLight(base.Parameters["DirLight2Direction"], base.Parameters["DirLight2DiffuseColor"], base.Parameters["DirLight2SpecularColor"], (cloneSource != null) ? cloneSource.light2 : null);
		}

		#region Public Methods
		/// <summary>
		/// Create a copy of this object.
		/// </summary>
		public override Effect Clone()
		{
			return new BasicEffect(this);
		}

		/// <summary>
		/// Enables default lighting for this effect.
		/// </summary>
		public void EnableDefaultLighting()
		{
			this.LightingEnabled = true;
		}
		#endregion
	}
}
